#include "StdAfx.h"
#include ".\itunes.h"

// WARNING, playlist position starts at 1 in this fucking interface!

CiTunes::CiTunes(void)
: refcount(0)
{
	InitializeCriticalSection(&cs);
	m_iTunesApp = NULL;
	_filter.EnableNotRespondingDialog(FALSE);
	_filter.EnableBusyDialog(FALSE);

	_filter.SetMessagePendingDelay(1000);
	_filter.SetRetryReply(1000);
	_filter.SetBusyReply(SERVERCALL_RETRYLATER);
	_filter.Register();
}

CiTunes::~CiTunes(void)
{
}

int CiTunes::Play(void) {
	if (Find() == FALSE)
		return 0;
	Connect();
	m_iTunesApp->Play();
	Disconnect();
	return 1;
}

int CiTunes::Stop(void) {
	if (Find() == FALSE)
		return 0;
	Connect();
	m_iTunesApp->Stop();
	Disconnect();
	return 1;
}

int CiTunes::Previous(void) {
	if (Find() == FALSE)
		return 0;
	Connect();
	m_iTunesApp->PreviousTrack();
	Disconnect();
	return 1;
}

int CiTunes::Next(void) {
	if (Find() == FALSE)
		return 0;
	Connect();
	m_iTunesApp->NextTrack();
	Disconnect();
	return 1;
}

int CiTunes::SetPosition(int ms) {
	if (Find() == FALSE)
		return 0;
	Connect();
	m_iTunesApp->put_PlayerPosition(ms / 1000);
	Disconnect();
	return 1;
}

int CiTunes::GetPosition(void) { // in ms
	if (Find() == FALSE)
		return 0;
	long pos = 0;
	Connect();
	m_iTunesApp->get_PlayerPosition(&pos);
	Disconnect();
	return (int) pos * 1000;
}

// pos is not used in this function
int CiTunes::Refresh(char *file, int pos) {
	pos++;
	if (Find() == FALSE || file == "" || pos < 0  || Connect() == 0)
		return 0;
	try {
		IITPlaylist *playlist;
		HRESULT res;
		selectPlaylist();
		res = m_iTunesApp->get_CurrentPlaylist(&playlist);
		IITTrackCollection *collection;
		if (res == S_OK)
			res = playlist->get_Tracks(&collection);
		if (res == S_OK)
		{
			IITTrack *track;
			res = collection->get_Item((int) pos, &track);
			if (res == S_OK)
			{
				IITFileOrCDTrack* f;
				if (track->QueryInterface(&f) == S_OK)
				{
					f->UpdateInfoFromFile();
					f->Release();
				}
			}
			if (res == S_OK)
				track->Release();
			res = S_OK;
		}
		if (res == S_OK)
			collection->Release();
		if (res == S_OK)
			playlist->Release();
	}
	catch (CException *e){e->ReportError();}
	Disconnect();
	return 1;
}

// TODO this sucks when there is no playlist selected!!
// have to add file to the lib!
// WARNING pos is not used due to interface shit
int CiTunes::PlAdd(char *file, int pos) { 
	pos++;
	if (Find() == FALSE || file == "" || pos < 0 || Connect() == 0)
		return 0;
	BSTR str = CString(file).AllocSysString();
	try {
		IITPlaylist *playlist;
		IITUserPlaylist *userPl;
		IITLibraryPlaylist *libPl;
		IITOperationStatus *operation;
		HRESULT res;
		selectPlaylist();

		res = m_iTunesApp->get_CurrentPlaylist(&playlist);
		if (res == S_OK)
			res = playlist->QueryInterface(&userPl);
		if (res == S_OK)
		{
			res = userPl->AddFile(str, &operation);
			userPl->Release();
			res = S_OK;
		}
		else
		{
			res = playlist->QueryInterface(&libPl);
			if (res == S_OK)
			{
				res = libPl->AddFile(str, &operation);
				libPl->Release();
				res = S_OK;
			}
		}

		if (res == S_OK)
		{
			operation->Release();
			playlist->Release();
		}
	}
	catch (CException *e){e->ReportError();}
	Disconnect();
	return 1;
}

int CiTunes::PlRemove(int pos) {
	pos++;
	if (Find() == FALSE || pos < 0 || Connect() == 0)
		return 0;
	IITPlaylist *playlist;
	HRESULT res;
	selectPlaylist();
	res = m_iTunesApp->get_CurrentPlaylist(&playlist);
	IITTrackCollection *collection;
	if (res == S_OK)
		res = playlist->get_Tracks(&collection);
	if (res == S_OK)
	{
		IITTrack *track;
		res = collection->get_Item((int) pos, &track);
		if (res == S_OK)		{
			res = track->Delete();
		}
		if (res == S_OK)
			track->Release();
		res = S_OK;
	}
	if (res == S_OK)
		collection->Release();
	if (res == S_OK)
		playlist->Release();
	Disconnect();
	return 1;
}
int CiTunes::PlSetPos(int pos) {
	pos++;
	if (Find() == FALSE || pos < 0 || Connect() == 0)
		return 0;
	IITPlaylist *playlist;
	HRESULT res;
	selectPlaylist();
	res = m_iTunesApp->get_CurrentPlaylist(&playlist);
	IITTrackCollection *collection;
	if (res == S_OK)
		res = playlist->get_Tracks(&collection);
	if (res == S_OK)
	{
		IITTrack *track;
		res = collection->get_Item((int) pos, &track);
		if (res == S_OK)
			res = track->Play();
		if (res == S_OK)
			track->Release();
		res = S_OK;
	}
	if (res == S_OK)
		collection->Release();
	if (res == S_OK)
		playlist->Release();
	Disconnect();
	return 1;
}
int CiTunes::PlGetPos(void) {
	if (Find() == FALSE || Connect() == 0)
		return 0;
	long index = 0;
	try {
		HRESULT res = NULL;
		selectPlaylist();
		IITTrack *currentTrack = NULL;
		if (m_iTunesApp != NULL)
			res = m_iTunesApp->get_CurrentTrack(&currentTrack); 
		if (res == S_OK && currentTrack != NULL)
		{			
			currentTrack->get_Index(&index);
			currentTrack->Release();
		}
	} catch (COleException *e){e->ReportError();}
	Disconnect();
	return (int) index - 1;
}

int CiTunes::PlGetSize(void) {
	if (Find() == FALSE || Connect() == 0)
		return 0;
	IITPlaylist *playlist;
	HRESULT res;
	selectPlaylist();
	res = m_iTunesApp->get_CurrentPlaylist(&playlist);
	IITTrackCollection *collection;
	if (res == S_OK)
		res = playlist->get_Tracks(&collection);
	long size = 0;
	if (res == S_OK)
		collection->get_Count(&size);
	if (res == S_OK)
		collection->Release();
	if (res == S_OK)
		playlist->Release();
	Disconnect();
	return (int) size;
}

int CiTunes::IsPlaying(void) {
	if (Find() == FALSE|| Connect() == 0)
		return 0;
	HRESULT res;
	selectPlaylist();
	ITPlayerState ps;
	res = m_iTunesApp->get_PlayerState(&ps);
	if (res == S_OK && ps == ITPlayerStatePlaying)
		return TRUE;
	Disconnect();
	return 0;
}

// WARNING pos is not used in here!!
CString CiTunes::GetFileName(int pos) {
	pos++;
	if (Find() == FALSE || pos < 0 || Connect() == 0)
		return "";
	CString temp = "";
	BSTR path = temp.AllocSysString();
	HRESULT res = NULL;
	selectPlaylist();
	IITTrack *track;
	if (m_iTunesApp != NULL)
		res = m_iTunesApp->get_CurrentTrack(&track);
	if (res == S_OK)
	{
		IITFileOrCDTrack* f;
		if (track->QueryInterface(&f) == S_OK)
		{
			f->get_Location(&path);
			f->Release();
		}
	}
	if (res == S_OK)
		track->Release();
	Disconnect();
	return (CString) path;
}

int CiTunes::Find(void) {
	if (::FindWindow("iTunes", NULL) == NULL)
		return 0;
	IRunningObjectTable*	rot;
	HRESULT hr = ::GetRunningObjectTable(0, &rot);
	IMoniker* moniker = NULL;
	IEnumMoniker* enumMoniker;

	CLSID clsid;
	IBindCtx* binder; 
	hr = ::CreateBindCtx(0, &binder);
	BSTR displayName; 

	hr = rot->EnumRunning( &enumMoniker ); 
	hr = enumMoniker->Next( 1, &moniker, NULL );
	while( hr == S_OK )
	{
		moniker->GetClassID( &clsid );
		moniker->GetDisplayName( binder, NULL, &displayName );
		if (CString(displayName) == "clsid:BBEB08F8-9126-4E20-AAD3-70B470144C7E:")
		{
			return 1;
		}
		hr = enumMoniker->Next( 1, &moniker, NULL );
	}
	return 0;
}
////////////////////////////////////////////////////////////////
// Connect/Disconnect the Ole object
////////////////////////////////////////////////////////////////
int CiTunes::Connect(void)
{
	EnterCriticalSection(&cs);
	if( !m_iTunesApp )
	{
		if( FAILED(::CoCreateInstance( CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, 
			IID_IiTunes, (void**)&m_iTunesApp )) )
		{
			LeaveCriticalSection( &cs );
			return 0;
		}
	}
	CiTunes::refcount++;
	LeaveCriticalSection(&cs);
	return 1;
}

void CiTunes::Disconnect(void)
{
	EnterCriticalSection(&cs);
    if( refcount == 0 ) 
	{
		LeaveCriticalSection(&cs);
		return;
	}
	if( --refcount == 0 && m_iTunesApp != NULL)
	{
		m_iTunesApp->Release();
		m_iTunesApp = NULL;
	}
	LeaveCriticalSection(&cs);
}

// WARNING: must be called inside a connect/disconnect block!
int CiTunes::selectPlaylist(void)
{
	if (Find() == FALSE)
		return FALSE;
	HRESULT res = 0;
	IITPlaylist *playlist = NULL;
	if (m_iTunesApp != NULL) 
		res = m_iTunesApp->get_CurrentPlaylist(&playlist);
	if (res != S_OK) // Just in case the playlist is not "selected"
	{
		m_iTunesApp->Play(); 
		m_iTunesApp->Stop();
	}
	else if (playlist != NULL)
		playlist->Release();
	return 0;
}
